/*
 * Copyright (C) 2013 The original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.zapta.apps.maniana.editors;

import java.text.DateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Hashtable;
import java.util.Map;

import android.app.DatePickerDialog;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.DatePicker;

import com.zapta.apps.maniana.R;
import com.zapta.apps.maniana.annotations.MainActivityScope;
import com.zapta.apps.maniana.editors.EditRepeatDay;
import com.zapta.apps.maniana.editors.EditRepeatWeek;
import com.zapta.apps.maniana.editors.EditRepeatMonth;
import com.zapta.apps.maniana.editors.EditRepeatYear;
import com.zapta.apps.maniana.main.MainActivityState;
import com.zapta.apps.maniana.main.MyApp;
import com.zapta.apps.maniana.model.ItemModel;
import com.zapta.apps.maniana.settings.PreferenceKind;
import com.zapta.apps.maniana.settings.PreferencesTracker;
import com.zapta.apps.maniana.util.LogUtil;
import com.zapta.apps.maniana.view.ExtendedEditText;


/**
 * Item text editor dialog. Used to create new itmes and to edit existing ones.
 * <p>
 * TODO: see if we can use the info in http://tinyurl.com/8yucabx to show the Done button on the
 * soft keyboard.
 *
 * @author Tal Dayan
 * @author Elliott Mitchell
 */
@MainActivityScope
public class ItemTextEditor extends FragmentActivity
implements DatePickerDialog.OnDateSetListener {

	Bundle mBundle;

	private static final boolean PRE_API_11 = android.os.Build.VERSION.SDK_INT < 11;

	public interface ItemEditorListener {
		void onDismiss(ItemModel item);
	}

	/** stores entries for items being edited */
	private static class EditEntry {
		public ItemModel item;
		public ItemEditorListener listener;
		EditEntry(ItemModel _item, ItemEditorListener _listener) {
			item = _item;
			listener = _listener;
		}
	}

	private static Map<String, EditEntry> mEdits = new Hashtable<String, EditEntry>();

	/** Stores the preference info we need to monitor */
	private PreferencesTracker mPrefTracker;

	/** For the extra values that are only valid if due date is set */
	private ItemTextEditorExtra mExtra;

	/** Private constructor. Use startEditor() to create and launch an editor. */
	@Override
	public void onCreate(Bundle saved) {
		super.onCreate(saved);

		// handle the situation of our home having been pressed
		if(saved!=null) getIntent().replaceExtras(saved.getBundle("intent_extras"));

		// TODO: This should really be reworked, can we get at the shared
		// preferences tracker?
		mPrefTracker = new PreferencesTracker(((MyApp)getApplication()).preferencesReader(),
				new PreferencesTracker.PreferenceChangeListener() {
					@Override
					public void onPreferenceChange(PreferenceKind preferenceKind) {
					}
				});

		mBundle=getIntent().getExtras();

		// TODO: reorder and organize the statements below for better reliability.
		setContentView(R.layout.editor_layout);
		setTitle(mBundle.getString(Intent.EXTRA_TITLE));


		// Get sub views
		final ExtendedEditText editText = (ExtendedEditText)
findViewById(R.id.editor_text);
		final ExtendedEditText editTextExt=(ExtendedEditText)
findViewById(R.id.editor_text_extended);

		final String text = mBundle.getString("item_text");
		final String textExt = mBundle.getString("item_text_ext");

		// Set text and style. Always using non completed variation, even
		// if the item is completed.
		editText.setText(text);
		editTextExt.setText(textExt);

		// Position cursor at the end of the text
		editText.setSelection(text.length());
		if(textExt!=null) editTextExt.setSelection(textExt.length());

		// For API 11+ these values come from the default theme. For holo,
		// this will be white text on dark gray background. This makes sure
		// the text cursor is at at color visible over the background.
		if (PRE_API_11) {
			final View topView = findViewById(R.id.editor_top);
			topView.setBackgroundColor(0xffffffff);
			editText.setBackgroundColor(0xffffffff);
			editTextExt.setBackgroundColor(0xffffffff);
			editText.setTextColor(0xff000000);
			editTextExt.setTextColor(0xff000000);
		}

		mPrefTracker.getPageItemFontVariation().apply(editText, false, false);
		mPrefTracker.getPageItemFontVariation().apply(editTextExt, false, false);

		// EditorEventAdapter eventAdapter = new EditorEventAdapter();

		editText.addTextChangedListener(new TextWatcher() {
			@Override
			public void afterTextChanged(Editable txt) {
				handleTextChanged(R.id.editor_text, txt.toString());
			}

			@Override
			public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
			}

			@Override
			public void onTextChanged(CharSequence txt, int arg1, int arg2, int arg3) {
				// handleTextChanged(R.id.editor_text, txt.toString());
			}
		});
		editTextExt.addTextChangedListener(new TextWatcher() {
			@Override
			public void afterTextChanged(Editable txt) {
				handleTextChanged(R.id.editor_text_extended, txt.toString());
			}

			@Override
			public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
			}

			@Override
			public void onTextChanged(CharSequence txt, int arg1, int arg2, int arg3) {
				// handleTextChanged(R.id.editor_text_extended, txt.toString());
			}
		});

		// TODO: why this setting does not work when done in the layout XML?
		editText.setHorizontallyScrolling(false);

		getWindow().setGravity(Gravity.TOP);

		// setup the due date button
		Button dueDateBut = (Button)findViewById(R.id.editor_due_date);
		final int dueDate = mBundle.getInt("item_due_date", 0);
		if(dueDate>0) {
			GregorianCalendar cal = new GregorianCalendar(
(dueDate>>>ItemModel.SHIFT_YEAR)&ItemModel.MASK_YEAR,
(dueDate>>>ItemModel.SHIFT_MONTH)&ItemModel.MASK_MONTH,
(dueDate>>>ItemModel.SHIFT_DAY)&ItemModel.MASK_DAY);
			dueDateBut.setText(DateFormat.getDateInstance().format(cal.getTime()));

			// bring in the extra editing bits
			mExtra = new ItemTextEditorDueExtras();
		} else mExtra = new ItemTextEditorExtras();

		mExtra.setArguments(mBundle);

		getSupportFragmentManager().beginTransaction().
replace(R.id.editor_extra, mExtra).commit();

	}

	/** Called when the home button is pressed. */
	@Override
	public void onPause() {
		super.onPause();
		final String action=mBundle.getString("editor_home");
		if(action.equals("remain")) return;
		getIntent().replaceExtras(mBundle);
		setResult(action.equals("quit_save")?RESULT_OK:RESULT_CANCELED,
getIntent());
		finish();
	}

	@Override
	public void onSaveInstanceState(Bundle outState) {
		outState.putBundle("intent_extras", mBundle);
	}

	/** Called when the dialog get dismissed. */
	@Override
	public void onBackPressed() {
		getIntent().replaceExtras(mBundle);
		setResult(getIntent().getBooleanExtra("back_save", false)?RESULT_OK:
RESULT_CANCELED, getIntent());
		// calls finish() (and setResult() too if you're not careful)
		super.onBackPressed();
	}

	private void done(boolean sucess) {
		getIntent().replaceExtras(mBundle);
		setResult(sucess?RESULT_OK:RESULT_CANCELED, getIntent());
		finish();
	}

	/** Called when text field value changes. */
	private final void handleTextChanged(final int id, final String value) {
		switch(id) {
		case R.id.editor_text:
			// We detect the Enter key by the '\n' char it inserts.
			if (value.contains("\n")) {
				final String cleanedValue = value.replace("\n", "").trim();
				mBundle.putString("item_text", cleanedValue);
				// This will trigger the handleOnDismiss above that will call
				// the listener of this editor.
				done(true);
			} else mBundle.putString("item_text", value);
		break;
		case R.id.editor_text_extended:
			mBundle.putString("item_text_ext", value);
		break;
		}
	}

	/** Called for the Save/Cancel buttons being pressed */
	public void buttonDone(View view) {
		done(view.getId()==R.id.editor_save?true:false);
	}

	/** Called for the due date button being pressed */
	public void buttonDueDate(View view) {
		DatePickerDialog dpd;

		int val = mBundle.getInt("item_due_date", 0);
		if(val>0) {
			dpd = new DatePickerDialog(this, this,
(val>>>ItemModel.SHIFT_YEAR)&ItemModel.MASK_YEAR,
(val>>>ItemModel.SHIFT_MONTH)&ItemModel.MASK_MONTH,
(val>>>ItemModel.SHIFT_DAY)&ItemModel.MASK_DAY);
		} else {
			GregorianCalendar cal = new GregorianCalendar();
			dpd = new DatePickerDialog(this, this, cal.get(Calendar.YEAR),
cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH));
		}

		dpd.show();
	}
	public void onDateSet(DatePicker View, int year, int month, int day) {
		GregorianCalendar cal = new GregorianCalendar(year, month, day);
		Button dueDate = (Button)findViewById(R.id.editor_due_date);
		dueDate.setText(DateFormat.getDateInstance().format(cal.getTime()));

		mBundle.putInt("item_due_date", year<<ItemModel.SHIFT_YEAR|
month<<ItemModel.SHIFT_MONTH|day<<ItemModel.SHIFT_DAY);

		if(mExtra.getClass() != ItemTextEditorDueExtras.class) {
			mExtra = new ItemTextEditorDueExtras();
			mExtra.setArguments(mBundle);
			getSupportFragmentManager().beginTransaction().
replace(R.id.editor_extra, mExtra).commit();
		}
	}
	public void buttonClearDueDate(View view) {
		Button dueDate = (Button)findViewById(R.id.editor_due_date);
		dueDate.setText("");
		mBundle.putInt("item_due_date", 0);

		if(mExtra.getClass() != ItemTextEditorExtras.class) {
			mExtra = new ItemTextEditorExtras();
			mExtra.setArguments(mBundle);
			getSupportFragmentManager().beginTransaction().
replace(R.id.editor_extra, mExtra).commit();
		}
	}

	/** implement ItemModel's store interface with an Intent */
	private static class StoreIF implements ItemModel.StoreIF {
		private final Intent mIntent;
		public StoreIF(Intent intent) {
			mIntent = intent;
		}

		public void setString(final String key, final String value) {
			mIntent.putExtra("item_"+key, value);
		}
		public void setBoolean(final String key, final boolean value) {
			mIntent.putExtra("item_"+key, value);
		}
		public void setByte(final String key, final byte value) {
			mIntent.putExtra("item_"+key, value);
		}
		public void setShort(final String key, final short value) {
			mIntent.putExtra("item_"+key, value);
		}
		public void setInt(final String key, final int value) {
			mIntent.putExtra("item_"+key, value);
		}
		public void setLong(final String key, final long value) {
			mIntent.putExtra("item_"+key, value);
		}

		public String getString(final String key) {
			return mIntent.getStringExtra("item_"+key);
		}
		public boolean getBoolean(final String key) {
			return mIntent.getBooleanExtra("item_"+key, false);
		}
		public byte getByte(final String key) {
			return mIntent.getByteExtra("item_"+key, (byte)0);
		}
		public short getShort(final String key) {
			return mIntent.getShortExtra("item_"+key, (short)0);
		}
		public int getInt(final String key) {
			return mIntent.getIntExtra("item_"+key, 0);
		}
		public long getLong(final String key) {
			return mIntent.getLongExtra("item_"+key, 0);
		}

		public boolean exists(final String key) {
			return mIntent.hasExtra("item_"+key);
		}
		public void delete(final String key) {
			mIntent.removeExtra("item_"+key);
		}
	}

	/**
	 * Show an item editor.
	 *
	 * @param mainActivityState mainActivityState context.
	 * @param title title to display in the editor.
	 * @param initialText initial edited item text
	 * @param listener listener to callback on changes and on end.
	 */
	public static void startEditor(final MainActivityState mainActivityState,
			int requestCode, String title, ItemModel item,
			final ItemEditorListener listener) {
		String id = item.getId();

		Intent intent = new Intent(mainActivityState.context(),
ItemTextEditor.class).putExtra(Intent.EXTRA_TITLE, title);
		StoreIF IF = new StoreIF(intent);
		item.toStore(IF);

		EditEntry entry = new EditEntry(item, listener);
		mEdits.put(id, entry);

		intent.putExtra("editor_home", mainActivityState.prefReader().
getEditorHomePreference()+"");
		intent.putExtra("back_save", mainActivityState.prefReader().
getBackEditorSavePreference());

		LogUtil.debug(3, "Starting editor on item \"%s\" (%d in-progress)", id,
mEdits.size());
		mainActivityState.mainActivity().startActivityForResult(intent,
requestCode);
	}

	/**
	 * Finish an item editor
	 *
	 * @param resultCode
	 * @param intent
	 */
	public static void endEditor(int resultCode, Intent intent) {
		if(intent==null) {
			LogUtil.warning("ItemTextEditor.endEditor() called with null intent!");
			return;
		}

		String id = intent.getStringExtra("item_id");
		StoreIF IF = new StoreIF(intent);

		if(id == null) {
			ItemModel item = new ItemModel(IF);
			LogUtil.debug(3, "Received item via Intent from outside");
		}

		EditEntry entry = mEdits.get(id);
		mEdits.remove(id);

		LogUtil.debug(3, "Finished editing item \"%s\" (%d left)", id,
mEdits.size());

	if(resultCode == RESULT_OK) {
		entry.item.fromStore(IF);
		entry.item.setIsCompleted(false);
		entry.listener.onDismiss(entry.item);
	}
	}
}

